package org.moonlight.jvm.rtda.share.heap.exception;

import lombok.Data;
import org.moonlight.jvm.classfile.attribute.CodeAttribute;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.symref.ClassRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

/**
 * 异常信息表
 *
 * @author Moonlight
 * @createTime 2023/11/9 17:55
 **/
public class ExceptionTable {

    private final ExceptionHandler[] exceptionTable;

    public ExceptionTable(CodeAttribute.ExceptionTableEntry[] entries, RtConstantPool rtCp) {
        this.exceptionTable = new ExceptionHandler[entries.length];
        for (int i = 0; i < entries.length; i++) {
            ExceptionHandler handler = new ExceptionHandler(
                    entries[i].getStartPc(),
                    entries[i].getEndPc(),
                    entries[i].getHandlerPc(),
                    getCatchType(entries[i].getCatchType(), rtCp)
            );
            this.exceptionTable[i] = handler;
        }
    }

    public ClassRef getCatchType(int catchType, RtConstantPool rtCp) {
        if (catchType == 0) {
            return null;
        }
        return (ClassRef) rtCp.getConstant(catchType);
    }

    public ExceptionHandler findExceptionHandler(Class exClass, int pc) {
        for (ExceptionHandler handler : exceptionTable) {
            if (pc >= handler.startPc && pc <= handler.endPc) {
                // 如果catchType为null，那么说明它在class文件中是0，表示可以处理所有异常，这是用来实现finally子句的
                if (handler.catchType == null) {
                    return handler;
                }
                Class catchClass = handler.catchType.resolvedClass();
                if (catchClass == exClass || catchClass.isSubClassOf(exClass)) {
                    return handler;
                }
            }
        }
        return null;
    }

    @Data
    public static class ExceptionHandler {
        int startPc;
        int endPc;
        int handlerPc;
        ClassRef catchType;

        ExceptionHandler(int startPc, int endPc, int handlerPc, ClassRef catchType) {
            this.startPc = startPc;
            this.endPc = endPc;
            this.handlerPc = handlerPc;
            this.catchType = catchType;
        }
    }


}